package com.gitee.linmt.filter.authentication;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.gitee.linmt.entity.UserInfo;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;

/**
 * 读取 Json 数据.
 *
 * @author <a href="mailto:lin-mt@outlook.com">lin-mt</a>
 */
public class JsonUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    
    public JsonUsernamePasswordAuthenticationFilter() {
        // 自定义该方式处理登录信息的登录地址，默认是 /login POST
        this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/loginProcessing", "POST"));
    }
    
    @Override
    public Authentication attemptAuthentication(final HttpServletRequest request, final HttpServletResponse response)
            throws AuthenticationException {
        // attempt Authentication when Content-Type is json
        // 如果请求携带的 ContentType 包含 UTF-8，自己在这里增加判断
        if (request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)) {
            //use jackson to deserialize json
            final ObjectMapper mapper = new ObjectMapper();
            UsernamePasswordAuthenticationToken authToken = null;
            try (final InputStream is = request.getInputStream()) {
                // 使用 jackson 从 request 中读取信息，并转成自定义的实体信息
                // 当然也可以直接从 InputStream 中读取字段值，萝卜青菜各有所爱
                final UserInfo userInfo = mapper.readValue(is, UserInfo.class);
                authToken = new UsernamePasswordAuthenticationToken(userInfo.getUsername(), userInfo.getPassword());
            } catch (final IOException e) {
                authToken = new UsernamePasswordAuthenticationToken("", "");
                throw new AuthenticationServiceException("Failed to read data from request", e.getCause());
            } finally {
                // 这一步主要是包装了请求地址和 sessionId
                setDetails(request, authToken);
            }
            // 进行登录信息的验证
            return this.getAuthenticationManager().authenticate(authToken);
        } else {
            // transmit it to UsernamePasswordAuthenticationFilter
            return super.attemptAuthentication(request, response);
        }
    }
}
